/**
 * @file shell_cpp.cpp
 * @author 独霸一方 (2696652257@qq.com)
 * @brief //> C++环境中初始化shell
 * @version 1.0
 * @date 2022-09-21
 *
 * @copyright Copyright (c) 2022
 *
 */

#include "shell_utils.hpp"

//+******************************** 串口DMA接收缓冲区以及先入先出队列 ***************************************/
using etl::array; //使用数组
using etl::queue; //使用先入先出队列

etl::array<uint8_t, 100> *arrayBuffer; //> 普通数组指针

etl::queue<uint8_t, 100> *fifoArray; //> 先入先出单端队列指针

//+******************************** 上面的数据结构关联FreeRTOS与部分硬件的 ***************************************/

/**
 * @brief 用户shell读
 *
 * @param data 数据
 * @param len 数据长度
 *
 * @return short 实际读取到
 */
short userShellRead(char *data, unsigned short len)
{
    if (true == fifoArray->empty()) //队列为空,直接返回
    {
        return 0;
    }
    else
    {
        int32_t count = 0; //记录实际写入多少长度

        //> 往传入的地址持续尝试写入len长度,这里可以用do while 结构,但是算了,麻烦
        while (false == fifoArray->empty() && count < len) //当队列不为空同时写入数小于设定时继续写入
        {
            *data = fifoArray->front(); //将队头数据赋值到传入地址
            fifoArray->pop();           //将队头数据弹出
            ++data;                     //地址位置往后移
            ++count;                    //写入数据计数增加
        }

        return count; //返回实际读取的个数,实际上shell中只会每次读取一个字符,不过还是按照标准写
    }
}

// C++环境的shell初始化
void userShellInit_cpp(void)
{
    fifoArray = (etl::queue<uint8_t, 100> *)pvPortMalloc(sizeof(etl::queue<uint8_t, 100>)); //> 创建在RTOS堆上,运行期间永不释放就不存在内存碎片化问题
    arrayBuffer = (etl::array<uint8_t, 100> *)pvPortMalloc(sizeof(etl::array<uint8_t, 100>));

    ::new (fifoArray) etl::queue<uint8_t, 100>(); //! 在开辟的内存地址调用默认构建函数
    ::new (arrayBuffer) etl::array<uint8_t, 100>();

    fifoArray->clear();   //先入先出队列清零
    arrayBuffer->fill(0); //缓冲数组清零

    //? 为什么要有这个函数?因为用到了C++的一些高级模板编程(ETL嵌入式模板库),所以原先的纯C环境无法做好兼容,需要将C++部分单独拿到C++环境中来做初始化

    shell.read = userShellRead; //绑定shell读取函数,C++环境的read函数,shell写函数就直接使用了C环境的串口阻塞函数

    userShellInit(); // C环境初始化shell
}

//+******************************** 添加log日志输出 ***************************************/

static SemaphoreHandle_t logMutex; //互斥锁,防止输出混乱

Log myLog = {
    .active = 1,
    .level = LOG_ALL}; //创建log对象,默认输出所有等级

/**
 * @brief 用户log上锁
 *
 * @param shell shell
 *
 * @return int 0
 */
int userLogLock(Log *log)
{
    xSemaphoreTakeRecursive(logMutex, portMAX_DELAY);
    return 0;
}

/**
 * @brief 用户log解锁
 *
 * @param shell shell
 *
 * @return int 0
 */
int userLogUnlock(Log *log)
{
    xSemaphoreGiveRecursive(logMutex);
    return 0;
}

/**
 * @brief 日志写函数实现
 *
 * @param buffer 数据
 * @param len 数据长度
 *
 */
void UserLogWrite(char *buffer, short len)
{
    if (myLog.shell)
    {
        shellWriteEndLine(myLog.shell, buffer, len);
    }
}

void userLogInit_cpp(void)
{
    logMutex = xSemaphoreCreateMutex();
    myLog.lock = userLogLock,
    myLog.unlock = userLogUnlock,

    myLog.write = UserLogWrite;

    logRegister(&myLog, &shell);
}
